Skip to content
00:00:00
0

Conditional 注解

一、Spring Boot @Conditional 注解

1.1 解决的问题

在一个智能的、自动配置的框架中,我们往往需要有条件地创建 Bean。例如:

  • 只有当类路径下存在某个类时,才创建某个 Bean。
  • 只有当配置文件中设置了某个特定属性时,才启用某个配置。
  • 只有当系统中没有其他同类型的 Bean 时,才创建默认的 Bean。

@Conditional 注解就是为了实现这种"条件化配置"而生的。它是 Spring Boot 自动配置的决策引擎。

1.2 核心

@Conditional 是 Spring Framework 4.0 引入的核心注解,它本身并不直接提供条件判断逻辑,而是接收一个或多个实现了 Condition 接口的类。

java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

Condition 接口只有一个方法:

java
public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
  • matches 方法返回 true ,则条件成立,Bean 会被创建。
  • matches 方法返回 false ,则条件不成立,Bean 不会被创建。
  • ConditionContext: 提供访问环境、类加载器、BeanFactory 等信息的上下文。
  • AnnotatedTypeMetadata: 提供访问被 @Conditional 注解的元素的元数据。

简单示例: 假设我们只想在 Window 操作系统上创建一个 Bean。

  1. 创建自定义 Condition:
java
public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 从环境信息中获取操作系统名称
        String osName = context.getEnvironment().getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("windows");
    }
}
  1. 使用 @Conditional 注解:
java
@Configuration
public class MyConfig {
    
    @Bean
    // 只有 Windows 系统下,此 Bean 才会被创建
    @Conditional(WindowsCondition.class) 
    public MyService windowsService() {
        return new WindowsService();
    }
}

二、@Conditional 衍生注解

1 @ConditionalOnBean 与 @ConditionalOnMissingBean

  • 根据 Spring 容器中是否存在某个 Bean 来决定配置。
java

@Bean
// 关键!只有当容器中没有 DataSource 类型的 Bean 时,才创建这个默认的 Bean
@ConditionalOnMissingBean 
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .build();
}

这个注解是实现 "默认配置" 和 "用户自定义配置覆盖" 的核心。

2 @ConditionalOnClass 与 @ConditionalOnMissingClass

  • 根据类路径下是否存在某个类来决定配置。
java

@Configuration
// 当类路径下存在 DataSource 类时,该配置类才生效
@ConditionalOnClass(DataSource.class) 
public class DataSourceAutoConfiguration {
    // ...
}

3 @ConditionalOnWebApplication 与 @ConditionalOnNotWebApplication

  • 根据当前应用是否是 Web 应用来决定配置。
java

@Configuration
// 只有在 Web 应用中,这个自动配置才生效
@ConditionalOnWebApplication 
public class WebMvcAutoConfiguration {
    // ...
}

4 @ConditionalOnWarDeployment 与 @ConditionalOnNotWarDeployment

  • 该注解仅在应用以传统WAR包方式部署时生效(即部署到Web服务器或应用服务器中),若应用使用嵌入式服务器(如内嵌的Tomcat),则条件不匹配,注解修饰的配置不会生效。
java

@Bean
@ConditionalOnWarDeployment
public WarWebService warWebService() {
    return new WarWebService();
}

5 @ConditionalOnProperty

  • 根据配置文件中的属性来决定配置。
java

@Bean
@ConditionalOnProperty(prefix = "app.feature", name = "cache.enabled", havingValue = "true")
public CacheService cacheService() {
    return new CacheService();
}

只有当 app.feature.cache.enabled=true 时,才会创建 CacheService

6 @ConditionalOnExpression

  • 使用 SpEL 表达式进行复杂条件判断。
java

@Bean
@ConditionalOnExpression("'${app.mode}'.equals('cluster') && ${app.nodes} > 1")
public ClusterService clusterService() {
    return new ClusterService();
}

7 @ConditionalOnResource

  • 只有当类路径下存在指定的资源文件时,配置才生效。
java

@Bean
@ConditionalOnResource(resources = "classpath:config/my-config.xml")
public MyService myService() {
    return new MyService();
}

8 @ConditionalOnJava

  • 指定某种java版本或者版本之前才能生效
java

@Bean
@ConditionalOnJava(range = "OLDER_THAN",value="TWENTY_ONE")
public MyService myService() {
    return new MyService();
}

只有当前运行的 java 版本小于 21 配置才生效

9 @ConditionalOnJndi

  • 依赖 JNDI 资源:只有当类路径中存在指定的 JNDI 资源(如 java:comp/env/jdbc/DataSource),目标 Bean 或配置类才会被加载。
  • 适用场景:常用于需要依赖 JNDI 数据源的配置,例如 JNDI 数据库连接池的自动配置。
java

@Configuration
@ConditionalOnJndi("java:comp/env/jdbc/DataSource")
public class JndiDataConfig {
    // 配置类定义
}

10 @ConditionalOnSingleCandidate

  • 唯一性校验‌:当容器中存在且仅存在一个指定类型的 Bean 时,条件成立,触发Bean或配置类的注册。
  • 适用场景:
    1. 依赖唯一 Bean‌:确保某个 Bean 的依赖项在容器中唯一(如 Primary Bean
    2. 可插拔实现‌:在多个实现类中选择唯一实现(需配合 @Primary 注解)
java

@Configuration
public class AnotherConfiguration {
    @Bean
    @ConditionalOnSingleCandidate(MyService.class)
    public AnotherBean anotherBean(MyService myService) {
        return new AnotherBean(myService);
    }
}

11 @ConditionalOnThreading

  • 线程模型适配:支持 JDK 21 的虚拟线程(Project Loom),通过配置Spring Boot的线程策略(如 spring.threads.virtual.enabled=true )决定且处于 active 状态是否加载特定组件。
  • 条件化配置:避免不同线程模型下的兼容性问题,例如针对虚拟线程优化异步处理逻辑。
java

@Configuration
public class MyThreadConfig {
    @Bean
    @ConditionalOnThreading(ThreadingType.VIRTUAL)
    public MyVirtualThreadAwareBean virtualThreadBean() {
        return new MyVirtualThreadAwareBean();
    }

    @Bean
    @ConditionalOnThreading(ThreadingType.PLATFORM)
    public MyPlatformThreadAwareBean platformThreadBean() {
        return new MyPlatformThreadAwareBean();
    }
}

12 @ConditionalOnCheckpointRestore

  • 存在类 org.crac.Resource 条件时判定成立
java

@Configuration
public class MyThreadConfig {
    @Bean
    @ConditionalOnCheckpointRestore
    public CheckpointRestore virtualThreadBean() {
        // ...
    }
}

13 @ConditionalOnCloudPlatform

  • spring 配置文件中指定了云平台时条件成立,例如: spring.main.cloud-platform=AZURE_APP_SERVICE
java

@Configuration
public class MyThreadConfig {
    @Bean
    @ConditionalOnCloudPlatform("AZURE_APP_SERVICE")
    public AZURCloudPlateformService virtualThreadBean() {
        // ...
    }
}

三、总结

条件注解判断条件
ConditionalOnClass类加载器中存在某个类
ConditionalOnMissingClass类加载器中不存在指定类
ConditionalOnBeanSpring 容器中存在某个指定 Bean
ConditionalOnMissingBeanSpring 容器中不存在某个指定 Bean
ConditionalOnSingleCandidateSpring 容器中是否存在且只存在一个对应的实例,或虽然有多个但是指定首选的 Bean 生效
ConditionalOnJava指定Java版本符合要求生效
ConditionalOnJndi存在JNDI
ConditionalOnCloudPlatform云平台,支持:CLOUD_FOUNDRY、HEROKU、SAP、NOMAD、KUBERNETES、AZURE_APP_SERVICE
ConditionalOnCheckpointRestore存在类 orc.crac.Resource
ConditionalOnWebApplicationWeb应用生效
ConditionalOnNotWebApplication不是Web应用生效
ConditionalOnWarDeploymentWar应用生效
ConditionalOnNotWarDeployment不是War应用生效
ConditionalOnResource当指定资源文件出现则生效
ConditionalOnProperty应用环境中的属性满足条件生效
ConditionalOnExpression判断SpEL表达式成立生效
ConditionalOnThreading指定线程处于active状态
最近更新